package no.bekk.boss.experior;

import static fitnesse.html.HtmlUtil.makeBreadCrumbsWithPageType;
import static fitnesse.html.HtmlUtil.makeInputTag;
import static fitnesse.html.HtmlUtil.makeJavascriptLink;
import static no.bekk.boss.experior.FitnesseMethodsExtractor.getWikiCommands;
import fitnesse.FitNesseContext;
import fitnesse.authentication.SecureOperation;
import fitnesse.authentication.SecureReadOperation;
import fitnesse.components.SaveRecorder;
import fitnesse.html.HtmlPage;
import fitnesse.html.HtmlTag;
import fitnesse.http.Request;
import fitnesse.http.Response;
import fitnesse.http.SimpleResponse;
import fitnesse.responders.SecureResponder;
import fitnesse.wiki.MockingPageCrawler;
import fitnesse.wiki.PageCrawler;
import fitnesse.wiki.PageData;
import fitnesse.wiki.PathParser;
import fitnesse.wiki.WikiPage;
import fitnesse.wiki.WikiPagePath;

/**
 * The Responder for requests with URL that ends on ?Experior.
 */
public class ExperiorResponder implements SecureResponder
{
	public static final String CONTENT_INPUT_NAME = "pageContent";
	public static final String SAVE_ID = "saveId";
	public static final String TICKET_ID = "ticketId";

	protected String content;
	protected WikiPage page;
	protected WikiPage root;
	protected PageData pageData;
	protected Request request;

	public ExperiorResponder()
	{

	}

	/**
	 * Make a standard response to a request. Uses the method doMakeResponse.
	 *
	 * @param FitNesseContext context
	 * @param Request request
	 * @see fitnesse.Responder#makeResponse(fitnesse.FitNesseContext, fitnesse.http.Request)
	 */
	public Response makeResponse(FitNesseContext context, Request request)
																		throws Exception {
		boolean nonExistent = request.hasInput("nonExistent") ? true : false;
		return doMakeResponse(context, request, nonExistent);
	}

	/**
	 * Make response if the requested page does not exist. Uses the method doMakeResponse.
	 *
	 * @param context
	 * @param request
	 */
	public Response makeResponseForNonExistentPage(FitNesseContext context,
													Request request) throws Exception {
		return doMakeResponse(context, request, true);
	}

	/**
	 * Creates a new SimpleResponse-object.
	 *
	 * @param context
	 * @param request
	 * @param firstTimeForNewPage
	 */
	protected Response doMakeResponse(FitNesseContext context, Request request,
										boolean firstTimeForNewPage) throws Exception {
		initializeResponder(context.root, request);

		SimpleResponse response = new SimpleResponse();
		String resource = request.getResource();
		WikiPagePath path = PathParser.parse(resource);
		PageCrawler crawler = context.root.getPageCrawler();
		if (!crawler.pageExists(root, path)) {
			crawler.setDeadEndStrategy(new MockingPageCrawler());
			page = crawler.getPage(root, path);
		} else
			page = crawler.getPage(root, path);

		pageData = page.getData();
		content = createPageContent();

		String html = doMakeHtml(resource, context);

		response.setContent(html);
		response.setMaxAge(0);

		return response;
	}

	/**
	 * Initializes the responder. Called from the method doMakeResponse().
	 *
	 * @param root
	 * @param request
	 */
	protected void initializeResponder(WikiPage root, Request request)
	{
		this.root = root;
		this.request = request;
	}

	/**
	 * Returns the content in the page generated by the Responder.*
	 *
	 */
	protected String createPageContent() throws Exception {
		return pageData.getContent();
	}

	/**
	 * Calls the method doMakeHtml
	 *
	 * @param resource
	 * @param context
	 */
	public String makeHtml(String resource, FitNesseContext context) throws Exception {
		return doMakeHtml(resource, context);
	}


	/**
	 * Creates a new HtmlPage-object, with title, JavaScript-reference and breadcrumbs.
	 * Calls the method makeEditForm.
	 *
	 * @param resource
	 * @param context
	 */
	public String doMakeHtml(String resource, FitNesseContext context) throws Exception	{
		HtmlPage html = context.htmlPageFactory.newPage();
		html.title.use("Edit with Experior " + resource);
		html.head.add(makeJavascriptLink( "/files/javascript/experior.js" ) );

		HtmlTag breadCrumbs =
		    makeBreadCrumbsWithPageType(resource, "Edit " +	"Page with Experior:");

		html.header.use(breadCrumbs);
		html.main.use(makeEditForm(resource));

		html.body.addAttribute("onload", "setInterval('checkClassName()', 1000)" );
		return html.html();
	}

	/**
	 * Creates a HtmlTag-object with a div-element. The div contains the textarea
	 * in Experior a hidden textfield. Calls methods necessary to create these elements.
	 *
	 * @param resource
	 */
	public HtmlTag makeEditForm( String resource ) throws Exception	{
		HtmlTag div = new HtmlTag("div");

		div.addAttribute("name", "placeholder");

		div.add( createHiddenField(resource) );
		div.add(createExperior( resource ) );

		return div;
	}

	/**
	 * Creates a HtmlTag-object with a form-element. Adds necessary attributes to the form,
	 * i.e. parameters is needed to save the content in Experior. Adds the textarea
	 * returned from the method createTextarea() to the form.
	 *
	 * @param resource
	 */
	private HtmlTag createExperior( String resource ) {
		HtmlTag form = new HtmlTag( "form" );
		form.addAttribute( "name", "f" );
		form.addAttribute("action", resource);
		form.addAttribute("method", "post");

		form.add(makeInputTag("hidden", "responder", "saveData"));
		form.add(makeInputTag("hidden", SAVE_ID, String.valueOf(SaveRecorder.newIdNumber())));
		form.add(makeInputTag("hidden", TICKET_ID, String.valueOf((SaveRecorder.newTicket()))));
		if (request.hasInput("redirectToReferer") && request.hasHeader("Referer")) {
			handleRedirect(form);
		}

		form.add( createTextarea() );

		return form;
	}

	/**
	 * Add redirect-parameter to the form created in createExperior().
	 *
	 * @param form
	 */
    private void handleRedirect(HtmlTag form) {

        String redirectUrl = request.getHeader("Referer").toString();
        int questionMarkIndex = redirectUrl.indexOf("?");
        if (questionMarkIndex > 0)
        	redirectUrl = redirectUrl.substring(0, questionMarkIndex);
        redirectUrl += "?" + request.getInput("redirectAction").toString();
        form.add(makeInputTag("hidden", "redirect", redirectUrl));
    }

    /**
     * Creates a HtmlTag-object with a textarea. Adds attributes necessary for the
     * JavaScript-file experior.js to transform this textarea to Experior. Place
     * the string contained in the variable content, which is the test, inside the
     * the textarea.
     */
	private HtmlTag createTextarea() {
		HtmlTag textarea = new HtmlTag( "textarea" );
		textarea.addAttribute("id", "experior");
		textarea.addAttribute("rows", "50");
		textarea.addAttribute("cols", "130");
		textarea.addAttribute("class", "experior");

		if( content.isEmpty() )
			textarea.add(" " );
		else
			textarea.add( content );
		return textarea;
	}

	/**
	 * Creates three HtmlTag-objects. One with a div-element and one with a textarea
	 * is placed inside a form-element. This contains Experiors hidden textarea.
	 *
	 * @param resource
	 */
	private HtmlTag createHiddenField( String resource ) {
		HtmlTag divHidden = new HtmlTag( "div" );
		divHidden.addAttribute("class", "hidden");

		HtmlTag form = new HtmlTag( "form" );
		form.addAttribute( "name", "foo" );
		form.addAttribute("id", "hiddenfieldform" );
		form.addAttribute("action", resource);
		form.addAttribute("method", "post");
		form.addAttribute("onsubmit", "moveText()" );

		form.add(makeInputTag("hidden", "responder", "save"));
		form.add(makeInputTag("hidden", SAVE_ID, String.valueOf(SaveRecorder.newIdNumber())));
		form.add(makeInputTag("hidden", TICKET_ID, String.valueOf((SaveRecorder.newTicket()))));
		form.add(makeInputTag("submit", "saveexit", "Save & exit" ) );
		form.add(makeInputTag("submit", "save", "Save") );

		if (request.hasInput("redirectToReferer") && request.hasHeader("Referer"))
		{
			handleRedirect(form);
		}

		form.add( createAlignButton() );

		HtmlTag textarea = new HtmlTag("textarea");
		textarea.addAttribute("class", CONTENT_INPUT_NAME);
		textarea.addAttribute("name", CONTENT_INPUT_NAME);
		textarea.addAttribute("id", "hiddenfield");
		textarea.addAttribute("rows", "30");
		textarea.addAttribute("cols", "70");
		textarea.addAttribute("tabindex", "1");
		textarea.add(getWikiCommands(content) );

		divHidden.add( textarea );

		form.add(divHidden);

		return form;
	}

	/**
	 * Creates a HtmlTag-object with an input-type=button.
	 * A click on this button will align all pipes in the test.
	 */
	private HtmlTag createAlignButton() {
		HtmlTag alignbutton = new HtmlTag("input");
		alignbutton.addAttribute("type", "button");
		alignbutton.addAttribute("value", "Align");
		alignbutton.addAttribute("name", "align");
		alignbutton.addAttribute("onClick", "alignClick()");
		return alignbutton;
	}


	/**
	 * Implemented method from the interface SecureResponder.
	 */
	public SecureOperation getSecureOperation()	{
		return new SecureReadOperation();
	}
}